﻿
namespace System.Reflection
{
    using System;
    using System.Linq;
    using System.Linq.Expressions;

    public class PropertyAccessor<TEntity, TValue> : IPropertyAccessor<TEntity, TValue>
    {
        #region Fields
        /// <summary>
        /// get方法委托
        /// </summary>
        private Func<TEntity, TValue> _getterFunc;

        /// <summary>
        /// set方法委托
        /// </summary>
        private Action<TEntity, TValue> _setterAction;

        /// <summary>
        /// 当前属性
        /// </summary>
        private readonly PropertyInfo _property;
        #endregion

        #region Ctor
        public PropertyAccessor(PropertyInfo property)
        {
            this._property = property;
        }
        #endregion

        #region Methods
        #region Inner Methods
        /// <summary>
        /// 加载Get委托
        /// </summary>
        private void InitializeGet()
        {
            if (!_property.CanRead)
            {
                return;
            }
            if (typeof(TValue) != typeof(object))
            {
                // 如果可以确定类型则直接创建委托
                this._getterFunc = Delegate.CreateDelegate(typeof(Func<TEntity, TValue>), _property.GetGetMethod())
                    as Func<TEntity, TValue>;
            }
            else
            {
                // 添加实例对象表达
                var instance = Expression.Parameter(typeof(TEntity), "instance");

                // 实例属性的访问表达式
                var instanceCast = Expression.Convert(instance, _property.ReflectedType);

                // 属性的访问表达式
                var propertyAccess = Expression.Property(instanceCast, _property);

                // 属性值的访问表达式
                var castPropertyValue = Expression.Convert(propertyAccess, typeof(TValue));

                // 生成表达式树
                var lambda = Expression.Lambda<Func<TEntity, TValue>>(castPropertyValue, instance);

                // 编译表达式树
                this._getterFunc = lambda.Compile();
            }
        }

        /// <summary>
        /// 加载Set委托
        /// </summary>
        private void InitializeSet()
        {
            if (!_property.CanWrite)
            {
                return;
            }

            if (typeof(TValue) != typeof(object))
            {
                // 如果可以确定类型则直接创建委托
                this._setterAction = Delegate.CreateDelegate(typeof(Action<TEntity, TValue>), _property.GetSetMethod())
                    as Action<TEntity, TValue>;
            }
            else
            {
                // 获取Set方法
                var methodInfo = _property.GetSetMethod();

                // 加载对象
                var instanceParameter = Expression.Parameter(typeof(TEntity), "instance");
                var parametersParameter = Expression.Parameter(typeof(TValue), "parameters");

                // 添加参数
                var paramInfos = methodInfo.GetParameters().FirstOrDefault();
                UnaryExpression valueCast = Expression.Convert(parametersParameter,paramInfos.ParameterType);

                // 添加对象的访问
                var instanceCast = Expression.Convert(instanceParameter, methodInfo.ReflectedType);

                // Set方法的访问
                var methodCall = Expression.Call(instanceCast, methodInfo, valueCast);

                // 创建表达式树
                var lambda = Expression.Lambda<Action<TEntity, TValue>>(
                            methodCall, instanceParameter, parametersParameter);

                // 编译表达式树
                this._setterAction = lambda.Compile();
            }
        }
        #endregion

        #region Interface Methods
        /// <summary>
        /// 取值方法
        /// </summary>
        /// <param name="instance">当前对象</param>
        /// <returns>获取的值</returns>
        public TValue GetValue(TEntity instance)
        {
            if (this._getterFunc == null)
            {
                this.InitializeGet();
                if (this._getterFunc == null)
                {
                    throw new ArgumentNullException("Get method is not defined for this property.");
                }
            }
            return this._getterFunc(instance);
        }

        /// <summary>
        /// 设定值方法
        /// </summary>
        /// <param name="instance">当前对象</param>
        /// <param name="value">需要设定的值</param>
        public void SetValue(TEntity instance, TValue value)
        {
            if (this._setterAction == null)
            {
                this.InitializeSet();
                if (this._setterAction == null)
                {
                    throw new ArgumentNullException("Set method is not defined for this property.");
                }
            }
            this._setterAction(instance, value);
        }
        #endregion
        #endregion
    }
}
